package uz.rt.dicom.data;

import uz.rt.dicom.ValueMultiplicity;
import uz.rt.dicom.ValueRepresentation;
import uz.rt.dicom.tag.DicomTag;

/**
 * Dicom data element can store any dicom attribute data and contains the following properties:
 * <ul>
 * <li>Dicom tag</li>
 * <li>Value multiplicity defining the range of fields that can be stored within the value field.</li>
 * <li>Length in bytes of the data fields</li>
 * </ul>
 *
 * @param <V> Value stored inside the data element
 */
public abstract class DataElement<V extends Comparable<? super V>> implements IDataElement
{
// ------------------------------ FIELDS ------------------------------

    /** Maximum length of the value (either in bytes or characters depending on the implementation). */
    protected int maxLength = 0;
    /** Dicom tag */
    protected DicomTag tag = null;
    /** Number of data fields that can be stored inside the value field of a dicom element. */
    protected ValueMultiplicity vm = null;
    /** Value representation of the data element */
    protected ValueRepresentation vr = null;
    /** Data value stored in the dicom element */
    protected V value = null;
    /** Backspace delimiter */
    protected String delimiter = "\\";

// --------------------------- CONSTRUCTORS ---------------------------

    //protected String delimiter = new StringBuilder().append('\\').toString();

    /**
     * Constructs a data element by its dicom tag. The ValueMultiplicity is set to null.
     *
     * @param tag Dicom tag
     */
    public DataElement(DicomTag tag)
    {
        this.tag = tag;
        this.vm = null;
    }

// --------------------- GETTER / SETTER METHODS ---------------------

    /**
     * Get the delimiter that separates the fields in the value fields of the dicom element.
     *
     * @return The delimiter that separates the fields in the value fields of the dicom element.
     */
    public String getDelimiter()
    {
        return delimiter;
    }

    /**
     * Set the delimiter that separates the fields in the value fields of the dicom element.
     *
     * @param delimiter The delimiter that separates the fields in the value fields of the dicom element.
     */
    public void setDelimiter(String delimiter)
    {
        this.delimiter = delimiter;
    }

    /**
     * Get the maximum length of the data element in bytes.
     *
     * @return Maximum length of the data element in bytes.
     */
    public int getMaxLength()
    {
        return maxLength;
    }

    /**
     * Set the maximum length of the data element in bytes.
     *
     * @param length Maximum length of the data element in bytes.
     */
    protected void setMaxLength(int length)
    {
        this.maxLength = length;
    }

    /**
     * Get the dicom tag
     *
     * @return Dicom tag
     */
    public DicomTag getTag()
    {
        return tag;
    }

    /**
     * Set the dicom tag
     *
     * @param tag Dicom tag
     */
    public void setTag(DicomTag tag)
    {
        this.tag = tag;
    }

    /**
     * Get the number of fields that can be stored in the data element
     *
     * @return Number of fields that can be stored in the data element
     */
    public ValueMultiplicity getVm()
    {
        return vm;
    }

    /**
     * Set the number of fields that can be stored in the data element
     *
     * @param vm Number of fields that can be stored in the data element
     */
    public void setVm(ValueMultiplicity vm)
    {
        this.vm = vm;
    }

// ------------------------ CANONICAL METHODS ------------------------

    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (!(o instanceof IDataElement)) return false;

        DataElement that = (DataElement) o;

        return !(delimiter != null ? !delimiter
                .equals(that.delimiter) : that.delimiter != null) && !(tag != null ? !tag
                .equals(that.tag) : that.tag != null) && !(vm != null ? !vm.equals(that.vm) : that.vm != null);
    }

    @Override
    public int hashCode()
    {
        int result = tag != null ? tag.hashCode() : 0;
        result = 31 * result + (vm != null ? vm.hashCode() : 0);
        result = 31 * result + (delimiter != null ? delimiter.hashCode() : 0);
        return result;
    }

    public String toString()
    {
        return value.toString();
    }

// ------------------------ INTERFACE METHODS ------------------------


// --------------------- Interface IDataElement ---------------------

    /**
     * Get the length of the dicom value.
     *
     * @return The length of the dicom value.
     */
    public abstract int getLength();

    /**
     * Get the value representation of the data element
     *
     * @return Value representation of the data element
     */
    public abstract byte[] getVR();

    /**
     * Get the value representation of the data element
     *
     * @return Value representation of the data element
     */
    public abstract ValueRepresentation getValueRepresentation();

    /**
     * Set the value representation of the data element
     *
     * @param vr Value representation of the data element
     */
    public void setValueRepresentation(ValueRepresentation vr)
    {
        this.vr = vr;
    }
}
